/*
 *所有关于IngredientList类的业务代码应在此处编写
*可使用repository.调用常用方法，获取EF/Dapper等信息
*如果需要事务请使用repository.DbContextBeginTransaction
*也可使用DBServerProvider.手动获取数据库相关信息
*用户信息、权限、角色等使用UserContext.Current操作
*IngredientListService对增、删、改查、导入、导出、审核业务代码扩展参照ServiceFunFilter
*/
using Mercury.Core.BaseProvider;
using Mercury.Core.Extensions.AutofacManager;
using Mercury.Entity.DomainModels;
using System.Linq;
using Mercury.Core.Utilities;
using System.Linq.Expressions;
using Mercury.Core.Extensions;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Http;
using Mercury.Order.IRepositories;
using System.Threading.Tasks;
using System.Collections.Generic;
using System;
using System.Reflection.Metadata;
using System.Xml.Linq;
using Mercury.Core.Common;

namespace Mercury.Order.Services
{
    public partial class IngredientListService
    {
        private readonly IHttpContextAccessor _httpContextAccessor;
        private readonly IIngredientListRepository _repository;//访问数据库

        [ActivatorUtilitiesConstructor]
        public IngredientListService(
            IIngredientListRepository dbRepository,
            IHttpContextAccessor httpContextAccessor
            )
        : base(dbRepository)
        {
            _httpContextAccessor = httpContextAccessor;
            _repository = dbRepository;
            //多租户会用到这init代码，其他情况可以不用
            //base.Init(dbRepository);
        }

        public async Task<WebResponseContent> GetIngredientListInfo(string orderNo)
        {
            var order = await repository.FindFirstAsync(c => c.Id == orderNo && c.DeletionFlag != 1);
            if (order == null)
                return Response.Error("订单不存在,或已经被处理！");

            var details = repository.DbContext.Set<IngredientListDetails>().Where(c => c.IngredientListId == order.Id).ToList();
            var orderElements = repository.DbContext.Set<IngredientListElement>().Where(c => c.IngredientListId == order.Id).ToList();

            var orderForm = repository.DbContext.Set<OrderForm>().FirstOrDefault(c => c.OrderNumber == order.OrderNumber);
            var queryable = repository.DbContext.Set<ProductData>().Where(c => c.Name == orderForm.SteelGrade && c.DeletionFlag != 1);
            var productElements = repository.DbContext.Set<ProductElementBOM>().Join(queryable, s => s.ProductId, t => t.Id, (s, t) => s).ToList();

            var elements = repository.DbContext.Set<BaseElement>().Where(c => c.DeletionFlag != 1).ToList();

            var data = new List<Dictionary<string, Object>>();

            var lowerLimitStandard = new Dictionary<string, Object>
            {
                { "MarterialName", orderForm.SteelGrade },
                { "CellStyle", new {
                    MarterialName= new Dictionary<string,string>
                    {
                        { "color","red"}      ,
                        { "font-weight","900"}
                    }
                } },
                { "Batch", "标准成分" }
            };
            var upperLimitStandard = new Dictionary<string, Object>
            {
                { "MarterialName", orderForm.SteelGrade },
                { "Batch", "标准成分" }
            };
            var lowerLimitIC = new Dictionary<string, Object>
            {
                { "MarterialName", orderForm.SteelGrade },
                { "Batch", "内控成分" }
            };
            var upperLimitIC = new Dictionary<string, Object>
            {
                { "MarterialName", orderForm.SteelGrade },
                { "Batch", "内控成分" }
            };
            var targetRate = new Dictionary<string, Object>
            {
                { "MarterialName", "配料成分" },
                { "Description", "目标成分" }
            };

            var total = order.PlannedFeedingQuantity.GetDouble();
            var targetProportion = new Dictionary<string, Object>
            {
                { "MarterialName", "配料成分" },
                { "Description", total }
            };
            var returnMaterial = details.Where(c => c.IngredientsType >= 2).Sum(c => c.RequiredWeight.GetDouble());
            total -= returnMaterial;
             var varField = new Dictionary<string, double>();
            foreach (var item in productElements)
            {
                var element = elements.First(c => c.Id == item.ElementId);
                varField.Add(element.ElementSymbols.ToLower(), 0);
                lowerLimitStandard[element.ElementSymbols.ToLower()] = item.UpperLimitStandard.HasValue ? (item.LowerLimitStandard > 0 ? item.LowerLimitStandard.ToString() : "≤") : item.LowerLimitStandard?.ToString();
                upperLimitStandard[element.ElementSymbols.ToLower()] = item.UpperLimitStandard;

                lowerLimitIC[element.ElementSymbols.ToLower()] = item.UpperLimitIC.HasValue ? (item.LowerLimitIC > 0 ? item.LowerLimitIC.ToString() : "≤") : item.LowerLimitIC?.ToString();
                upperLimitIC[element.ElementSymbols.ToLower()] = item.UpperLimitIC;
                targetRate[element.ElementSymbols.ToLower()] = item.TargetProportion;
                targetProportion[element.ElementSymbols.ToLower()] = (item.TargetProportion.GetDouble() * total / 100).GetDouble().ToString("F2");
            }

            data.Add(lowerLimitStandard);
            data.Add(upperLimitStandard);
            data.Add(lowerLimitIC);
            data.Add(upperLimitIC);
            data.Add(targetRate);
            data.Add(targetProportion);

            data.Add(new Dictionary<string, object> {
                { "MarterialName", "名称" },
                { "CellStyle", new {
                    MarterialName= new {color = "red"},
                        Batch    = new {color = "blue"}
                } },
                { "Batch", "批号" }});

            var computedSum = new Dictionary<string, Object>
                {
                    { "MarterialName", "合计" }   ,
                    { "Description",  "计算重量" }
                };

            var realSum = new Dictionary<string, Object>
                {
                    { "MarterialName", "合计" }   ,
                    { "Description",  "实际重量" }
                };
            var computedGoldContent = new Dictionary<string, Object>
                {
                    { "MarterialName", "合计" }  ,
                    { "Description",  "计算含金量" }
                };
            var realGoldContent = new Dictionary<string, Object>
                {
                    { "MarterialName", "合计" }  ,
                    { "Description",  "实际含金量" }
                };
            var computed = new Dictionary<string, Object>
                {
                    { "MarterialName", "合计" }    ,
                    { "Description",  details.Sum(c=>c.RequiredWeight.ToDecimal()) }
                };
            var real = new Dictionary<string, Object>
                {
                    { "MarterialName", "合计" }   ,
                    { "Description",  details.Sum(c=>c.ActualWeight.ToDecimal()) }
                };

            foreach (var item in details.Where(c => c.IngredientsType < 2).OrderBy(c => c.MaterialId).ThenBy(c => c.BatchNumber))
            {
                var materialElementRate = new Dictionary<string, Object>()
                {
                    { "MarterialName", item.MaterialName },
                    { "Batch", item.BatchNumber },
                    { "Description", "重量" }
                };
                var materialElementAmount = new Dictionary<string, Object>()
                {
                    { "MarterialName", item.MaterialName },
                    { "Batch", item.BatchNumber },
                    { "Description",  item.RequiredWeight }
                };
                var realMaterialElementRate = new Dictionary<string, Object>()
                {
                    { "MarterialName", item.MaterialName },
                    { "Batch", item.BatchNumber },
                    { "Description",  "实际重量" }
                }; ;
                var realMaterialElementAmount = new Dictionary<string, Object>()
                {
                    { "MarterialName", item.MaterialName },
                    { "Batch", item.BatchNumber },
                    { "Description",  item.ActualWeight }
                };
                foreach (var ele in orderElements.Where(c => c.IngredientListDetailsId == item.Id))
                {
                    var element = elements.First(c => c.Id == ele.ElementId);
                    varField[element.ElementSymbols.ToLower()] += ele.ActualWeight.GetDouble();
                    materialElementRate.Add(element.ElementSymbols.ToLower(), ele.ElementContent);
                    materialElementAmount.Add(element.ElementSymbols.ToLower(), ele.ActualWeight);
                    realMaterialElementRate.Add(element.ElementSymbols.ToLower(), ele.ElementContent);
                    realMaterialElementAmount.Add(element.ElementSymbols.ToLower(), (item.ActualWeight ?? "0").ToDecimal() * ele.ElementContent / 100);
                }
                data.Add(materialElementRate);
                data.Add(materialElementAmount);
                data.Add(realMaterialElementRate);
                data.Add(realMaterialElementAmount);

            }

            var realTotal = details.Sum(c => c.ActualWeight.GetDouble());
            var realData = orderElements.Join(details, s => new { s.MaterialId, s.BatchNumber }, t => new { t.MaterialId, t.BatchNumber }, (s, t) => new
            {
                ActualWeight = string.IsNullOrEmpty(t.ActualWeight) ? "0" : (t.ActualWeight.ToDecimal() * s.ElementContent / 100).ToString(),
                s.ElementId
            }).ToList().GroupBy(c => c.ElementId).Select(c => new
            {
                ElementId = c.Key,
                Element = elements.First(x => x.Id == c.Key).ElementSymbols.ToLower(),
                ActualWeight = c.Sum(x => x.ActualWeight.GetDouble())
            }).ToList();

            foreach (var item in varField)
            {
                var realElement = realData.First(c => c.Element == item.Key);
                var bom = productElements.First(c => c.ElementId == realElement.ElementId);
                computedSum.Add(item.Key, item.Value.ToString("F2"));
                var computedRate = (item.Value * 100 / total.GetDouble()).ToString("F6").GetDouble();
                computedGoldContent.Add(item.Key, computedRate);

                var weight = realElement.ActualWeight.ToString("F2").GetDouble(); ;
                var realRate = (weight * 100 / realTotal).ToString("F6").GetDouble();
                realSum.Add(item.Key, weight);
                realGoldContent.Add(item.Key, realRate);
                var limit = ProductMaterialCalculationHelper.GetLimit(bom);
                if (limit.Item1 == 0)
                    continue;
                var color = string.Empty;
                if (realRate > limit.Item1)
                    color = "red";

                else if (realRate < limit.Item2)
                    color = "blue";
                FormatCellStyle(realSum, color, item.Key);
                FormatCellStyle(realGoldContent, color, item.Key);
            }

            void FormatCellStyle(Dictionary<string, object> formatDic, string color, string field)
            {
                if (!string.IsNullOrEmpty(color))
                {
                    if (!formatDic.ContainsKey("CellStyle"))
                    {
                        formatDic.Add("CellStyle", new Dictionary<string, object>
                        {
                            { field,new Dictionary<string, string>
                            {
                                { "color",color}      ,
                                { "font-weight","900"}
                            } }
                        });
                    }
                    else
                    {
                        var dic = formatDic["CellStyle"] as Dictionary<string, object>;
                        dic.Add(field, new Dictionary<string, string>
                            {
                                { "color",color}      ,
                                { "font-weight","900"}
                            });
                    }
                }
            }

            data.Add(computedSum);
            data.Add(realSum);
            data.Add(computedGoldContent);
            data.Add(realGoldContent);
            data.Add(computed);
            data.Add(real);

            return Response.OK("", new
            {
                varField = varField.Keys.ToList(),
                data
            });
        }


        public async Task<WebResponseContent> ConfrimBill(string orderNo) 
        {
            var bill =await _repository.FindAsyncFirst(c=>c.Id == orderNo);
            if (bill == null)
                return Response.Error("配料单异常！");

            if(bill.State == 5)
                return Response.Error("配料单已确认，请勿重复操作！");

            bill.State = 5;
            bill.SetModifyDefaultVal();

            _repository.Update(bill,true);
            return Response.OK("配料单确认完成！");
        }
    }
}
